﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Nirvana
{
    [DisallowMultipleComponent]
    public sealed class Scheduler : MonoBehaviour
    {
        private struct TaskAction
        {
            public float time;
            public Action task;
            public bool Remove(TaskAction taskaction)
            {
                if(this.time >= taskaction.time)
                {
                    removeTaskList.Add(taskaction.task);
                    return true;
                }
                return false;
            }
        }

        private static Scheduler scheduler;
        private static Logger logger = LogSystem.GetLogger("Scheduler");
        private static LinkedList<Action> linkedlist = new LinkedList<Action>();
        private static List<TaskAction> taskactionList = new List<TaskAction>();
        private static List<Action> waitTaskList = new List<Action>();
        private static List<Action> removeTaskList = new List<Action>();
        private static List<Action> delayTaskList = new List<Action>();


        [RuntimeInitializeOnLoadMethod]
        public static void Init()
        {
            if(scheduler == null && Application.isPlaying)
            {
                GameObject gameObject = new GameObject("Scheduler", typeof(Scheduler));
                DontDestroyOnLoad(gameObject);
                scheduler = gameObject.GetComponent<Scheduler>();
            }
        }

        void Awake()
        {
            ClearCache();
            DontDestroyOnLoad(this);
        }

        void Update()
        {
            LinkedListNode<Action> next;
            for(var node = linkedlist.First; node!= null;node = next)
            {
                next = node.Next;
                Action action = node.Value;
                try
                {
                    action();
                }catch(Exception ex)
                {
                    logger.LogError(ex.ToString());
                    linkedlist.Remove(node);
                }
            }
            lock (waitTaskList)
            {
                if(waitTaskList.Count > 0)
                {
                    for(int i = 0; i < waitTaskList.Count; i++)
                    {
                        removeTaskList.Add(waitTaskList[i]);
                    }
                    waitTaskList.Clear();
                }
            }

            if(delayTaskList.Count > 0)
            {
                for (int j = 0; j < delayTaskList.Count; j++) removeTaskList.Add(delayTaskList[j]);
                delayTaskList.Clear();
            }

            if(taskactionList.Count  > 0)
            {
                TaskAction task = new TaskAction();
                task.time = Time.realtimeSinceStartup;
                taskactionList.RemoveAll(task.Remove);
            }
            Clear();

            ReferenceDict.Update();
        }

        void OnDestroy()
        {
            linkedlist.Clear();
            taskactionList.Clear();
            waitTaskList.Clear();
            removeTaskList.Clear();
            delayTaskList.Clear();
        }

        void ClearCache()
        {
            linkedlist.Clear();
            taskactionList.Clear();
            waitTaskList.Clear();
            removeTaskList.Clear();
            delayTaskList.Clear();
        }

        private static Scheduler GetScheduler()
        {
            Init();
            return scheduler;
        }

        public static LinkedListNode<Action> AddFrameListener(Action action)
        {
            return linkedlist.AddLast(action);
        }

        public static void RemoveFrameListener(LinkedListNode<Action> handle)
        {
            linkedlist.Remove(handle);
        }

        public static Coroutine RunCoroutine(IEnumerator coroutine)
        {
            return GetScheduler().StartCoroutine(coroutine);
        }

        public  static void StopCoroutines()
        {
            GetScheduler().StopAllCoroutines();
        }

        public static void StopShcCoroutine(Coroutine coroutine)
        {
            GetScheduler().StopCoroutine(coroutine);
        }

        public static void PostTask(Action task)
        {
            lock (waitTaskList)
            {
                waitTaskList.Add(task);
            }
        }

        public static void Delay(Action task)
        {
            delayTaskList.Add(task);
        }

        public static void Delay(Action task,float time)
        {
            TaskAction action = default(TaskAction);
            action.task = task;
            action.time = Time.realtimeSinceStartup + time;
            taskactionList.Add(action);
        }


        private void Clear()
        {
            for(int i=0;i< removeTaskList.Count; i++)
            {
                try
                {
                    removeTaskList[i]();
                }
                catch (Exception ex)
                {
                    logger.LogError(ex.ToString());
                }
            }

            removeTaskList.Clear();
        }


    }
}

